package vista.paneles.hojaDibujo;

import java.awt.Dimension;
import java.awt.Point;
import java.util.Iterator;
import java.util.LinkedList;

import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLJPanel;
import javax.swing.JComponent;

import controlador.ControladorEditor;

import eventos.EventoHojaDibujo;

import vista.Tema;
import vista.objetos.DiagramaGraficable;
import vista.objetos.GraficableConEtiqueta;
import vista.objetos.GraficableConEtiquetaConAtributos;
import vista.objetos.JerarquiaGraficable;
import vista.paneles.TipoEvento;

public class HojaDeDibujo implements GLEventListener
{
    private GLJPanel glcanvas;

    private Point posMouse = new Point();
   
    private ControladorDeSeleccion controladorSeleccion;
    
    private DiagramaGraficable diagramaActual;
    private GraficableConEtiqueta graficableFlotante = null;
    
    private int escalado = 1;
    private final int ESCALADO_MAX = 3;
    private final int ESCALADO_MIN = 1;
    private boolean seCambioLaEscala = false;
    private int tamLetra = Tema.getInstancia().getFuenteTamano();
    
    private Dimension tamView = new Dimension();
    
    private Dimension tamHoja = new Dimension();
    private int factorOrientacion = 2;
    private OrientacionHoja orientacionHoja;
    
    private TipoEvento tipoEvento = TipoEvento.AGREGAR_GRAFICABLE_NUEVO;
    
    public HojaDeDibujo(DiagramaGraficable diagrama)
    {
        GLProfile profile = GLProfile.get(GLProfile.GL2);
        GLCapabilities capabilities = new GLCapabilities(profile);
        
        this.glcanvas = new GLJPanel(capabilities);
        this.glcanvas.addGLEventListener(this);
        this.cambiarSentidoDeHoja(OrientacionHoja.SENTIDO_VERTICAL);
        this.glcanvas.setLayout(null);  
        
        this.setControladorSeleccion(new ControladorDeSeleccion());
        this.setDiagramaActual(diagrama);
       
    }
    
    public void setManejadorDeEventos(EventoHojaDibujo eventos)
    {
    	this.getCanvas().addMouseListener(eventos);
    	this.getCanvas().addMouseMotionListener(eventos);
    }
    
    public void setGraficableAAgregar(GraficableConEtiqueta graficable, TipoEvento tipoEvento)
    {
        if (this.graficableFlotante != null)
                graficableFlotante.borrarseDeLaHoja();
        
        this.graficableFlotante = graficable;
        this.setTipoEvento(tipoEvento);
        this.graficableFlotante.getPunto().setLocation(this.getPosicionMouse());
        this.graficableFlotante.inicializarPosiciones();
        this.graficableFlotante.agregarseAlCanvas(getCanvas(), Tema.getInstancia().getFuenteGral(tamLetra), Tema.getInstancia().getFuenteColor());
        this.glcanvas.repaint();
    }

    public void setJerarquiaAAgregar(JerarquiaGraficable jerarquia)
    {
    	Tema tema = Tema.getInstancia();
        jerarquia.agregarseAlCanvas(this.getCanvas(), tema.getFuenteGral(tamLetra), tema.getFuenteColor());
        this.glcanvas.repaint();
    }
    
    public GLJPanel getCanvas() {
        return this.glcanvas;
    }
 
    public void init(GLAutoDrawable gLDrawable) 
    {
        GL2 gl = gLDrawable.getGL().getGL2();
        gl.glClearColor(0.48f, 0.48f, 0.48f, 0.0f);
        gl.glShadeModel(GL2.GL_SMOOTH);
    }
    
    public void display(GLAutoDrawable gLDrawable) 
    {
        final GL2 gl = gLDrawable.getGL().getGL2();     
        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
       
        setViewPort(gl);   
        dibujarHoja(gl);       
        escalar();
        
        this.diagramaActual.dibujarse(gLDrawable);
        dibujarGraficableFlotante(gLDrawable);
        
        
        //tema de superposición
        LinkedList<JComponent> listaEtiquetas = new LinkedList<JComponent>();
        for(Iterator<GraficableConEtiqueta> it = this.getDiagramaActual().getGraficables().iterator(); it.hasNext() == true;)
               listaEtiquetas.add(it.next().getEtiqueta());
        
        for(Iterator<JComponent> it = listaEtiquetas.iterator(); it.hasNext() == true;)
        {
                JComponent etiqueta = it.next();
                if (UtilHojaDeDibujo.seSuperponeConAlgunComponente(etiqueta, listaEtiquetas)) {
                        //TODO: Set fuente para objetos superpuesto
                        etiqueta.setForeground(Tema.getInstancia().getFuenteColor());
                }
                else {
                        //TODO: Set fuente para objetos NO superpuesto
                }
        }
        
        this.controladorSeleccion.dibujarSeleccion(gl);

        gl.glFlush();
    }
 
    private void dibujarGraficableFlotante(GLAutoDrawable gLDrawable)
    {
    	if (graficableFlotante == null)
    		return;

    	this.graficableFlotante.setUbicacionEtiqueta(this.getPosicionMouse().x - graficableFlotante.getEtiqueta().getWidth()/2 ,
    			this.getPosicionMouse().y - graficableFlotante.getEtiqueta().getHeight()/2);
    	this.graficableFlotante.dibujarse(gLDrawable);
    }

    private void escalar() 
    {
    	if (!seCambioLaEscala)
    		return;
    	
        for(Iterator<GraficableConEtiqueta> it = this.getDiagramaActual().getGraficables().iterator(); it.hasNext() == true;)
        	it.next().cambiarEscala(escalado);
    	                      
    	seCambioLaEscala = false;
    }

    private void dibujarHoja(final GL2 gl)
    {
    	gl.glColor3fv(Tema.getInstancia().getHojaColor());
    	gl.glRectf(0.0f, 0.0f, tamHoja.width, tamHoja.height);
    }

    public void agregarGraficableFijo() {
        this.graficableFlotante = null;         
    }
    
    public void displayChanged(GLAutoDrawable gLDrawable, boolean modeChanged, boolean deviceChanged) {
//      System.out.println("displayChanged called");
    }
 
    public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, int height) {
        this.tamView.setSize(width, height);
    }
    
    private void setViewPort(final GL2 gl) 
    {
       gl.glViewport(0, 0, tamView.width, tamView.height);
       gl.glMatrixMode(GL2.GL_PROJECTION);
       gl.glLoadIdentity();

       UtilHojaDeDibujo.glu.gluOrtho2D(0.0f, tamHoja.width, 0.0f, tamHoja.height);
       gl.glMatrixMode(GL2.GL_MODELVIEW);
       gl.glLoadIdentity();
    }
  
    public void dispose(GLAutoDrawable arg0) { /* System.out.println("dispose() called");*/ }

    public GraficableConEtiqueta getGraficableFlotante(){
    	return this.graficableFlotante;
    }
        
    public Point getPosicionMouse(){
    	return this.posMouse;
    }
    
    public OrientacionHoja getOrientacion() {
    	return this.orientacionHoja;
    }
        
    public void cambiarSentidoDeHoja()
    {
    	if (this.orientacionHoja == OrientacionHoja.SENTIDO_VERTICAL)
    		this.cambiarSentidoDeHoja(OrientacionHoja.SENTIDO_HORIZONTAL);
    	else
    		this.cambiarSentidoDeHoja(OrientacionHoja.SENTIDO_VERTICAL);
    	
    	if (this.diagramaActual!=null)
    		this.diagramaActual.setOrientacion(orientacionHoja);
    }
        
    private void cambiarSentidoDeHoja(OrientacionHoja orientacion) 
    {
    	this.orientacionHoja = orientacion;
    	this.setSentidoDeHoja();           
    }
        
    private void setSentidoDeHoja()
    {
    	if(orientacionHoja == OrientacionHoja.SENTIDO_VERTICAL)
    	{
    		tamHoja.width = Tema.getInstancia().getHojaAncho();
    		tamHoja.height = Tema.getInstancia().getHojaAlto();
    	} 
    	else
    	{
    		tamHoja.width = Tema.getInstancia().getHojaAlto();
    		tamHoja.height = Tema.getInstancia().getHojaAncho();
    	}

    	setDimensionesCanvas();
    }

    private void setDimensionesCanvas()
    {
    	this.getCanvas().setPreferredSize(new Dimension(tamHoja.width * factorOrientacion * escalado, tamHoja.height * factorOrientacion * escalado));
    	this.getCanvas().setSize(new Dimension(tamHoja.width * factorOrientacion * escalado, tamHoja.height * factorOrientacion * escalado));
    }
    
    public DiagramaGraficable getDiagramaActual(){
    	return diagramaActual;
    }
        
    public void setDiagramaActual(DiagramaGraficable diagrama)
    {
    	limpiarCanvas();
    	this.diagramaActual = diagrama;
    	this.cambiarSentidoDeHoja(this.diagramaActual.getOrientacion());
    	resetearCanvas();
    	resetearControladorSeleccion();
        agregarComponentesAlCanvas();
    }

	private void resetearControladorSeleccion()
	{
		this.controladorSeleccion.setDiagramaActual(this.diagramaActual);
		this.controladorSeleccion.cancelarSeleccion();
	}

	private void resetearCanvas()
	{
		this.graficableFlotante = null;
    	this.escalado = ESCALADO_MIN;
        this.seCambioLaEscala = false;
    	this.setDimensionesCanvas();
    	this.posMouse = new Point();
	}

	private void limpiarCanvas() 
	{
		if (this.getDiagramaActual()!= null)
			this.getDiagramaActual().borrarseDeLaHoja();
	}

    private void agregarComponentesAlCanvas() 
    {
    	Tema tema = Tema.getInstancia();
    	Iterator<GraficableConEtiqueta> it = this.getDiagramaActual().getGraficables().iterator(); 
                
    	while (it.hasNext())
    	{
    		GraficableConEtiqueta objeto = it.next();
    		objeto.agregarseAlCanvas(this.getCanvas(), tema.getFuenteGral(tamLetra), tema.getFuenteColor());
    		objeto.agregarEventoEtiqueta(this);
    		objeto.setPopupMenu();
    		objeto.getPopupMenu().escucharEventoEditar(objeto.getEventoEditarGraficable(ControladorEditor.getInstancia().getVentanaPrograma()));
        }
    }

    public int getEscalado() {
    	return escalado;
    }

    public void resetearEscalado() 
    {
    	this.escalado = 1;
    	this.setDimensionesCanvas();
    	this.seCambioLaEscala = true;
    }
    
    public void aumentarEscalado() 
    {
    	if (this.escalado < this.ESCALADO_MAX )
    	{
    		this.escalado++;
    		this.setDimensionesCanvas();
    		seCambioLaEscala = true;
    	}
    }

    public void reducirEscalado()
    {
    	if (this.escalado > this.ESCALADO_MIN)
    	{
    		this.escalado--;
    		this.setDimensionesCanvas();
    		seCambioLaEscala = true;
    	}
    }

    public TipoEvento getTipoEvento() {
    	return tipoEvento;
    }

    private void setTipoEvento(TipoEvento tipoEvento) {
    	this.tipoEvento = tipoEvento;
    }

    public void cancelarAcciones() 
    {
    	if (this.graficableFlotante != null)
    	{
    		this.graficableFlotante.borrarseDeLaHoja();
    		this.graficableFlotante = null;
    	}
    }       

	public ControladorDeSeleccion getControladorSeleccion() {
		return controladorSeleccion;
	}

	public void setControladorSeleccion(ControladorDeSeleccion controladorSeleccion) {
		this.controladorSeleccion = controladorSeleccion;
	}
	
	public void actualizarGraficableConAtributo(GraficableConEtiquetaConAtributos graf)
	{
		Tema tema = Tema.getInstancia();
		
        graf.inicializarPosiciones();
        graf.agregarseAlCanvas(getCanvas(), tema.getFuenteGral(tamLetra), tema.getFuenteColor());
        graf.agregarEventoEtiqueta(this);
	}
}